/*
 * © 2024 huayunliufeng保留所有权利, 依据MIT许可证发布。
 * 请勿更改或删除版权声明或此文件头。
 * 此代码是免费软件, 您可以重新分发和/或修改它。
 * 开源是希望它有用, 但不对代码做任何保证。
 * 如有疑问请联系: huayunliufeng@163.com
 */

package io.github.huayunliufeng.dbinfo.dbinfo;

import io.github.huayunliufeng.common.utils.HylfDataUtil;
import io.github.huayunliufeng.common.utils.HylfFunUtil;
import io.github.huayunliufeng.dbinfo.model.PrivilegeInfo;
import io.github.huayunliufeng.dbinfo.model.TableColumnInfo;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;

/**
 * 获取字段信息。
 *
 * @author huayunliufeng
 * @date_time 2024/3/23 22:43
 */
@Slf4j
public class ObtainColumnInfo extends ObtainDbInfo {

    private static Map<Connection, ObtainColumnInfo> obtainColumnInfoMap = null;

    private Map<String, List<TableColumnInfo>> columnInfoMap = null;

    private Map<String, List<PrivilegeInfo>> columnPrivilegeInfoMap = null;

    protected ObtainColumnInfo(final Connection conn) {
        super(conn);
    }

    public static ObtainColumnInfo build(final Connection conn) {
        obtainColumnInfoMap = HylfFunUtil.mapAddValue(obtainColumnInfoMap, conn, () -> new ObtainColumnInfo(conn));
        return obtainColumnInfoMap.get(conn);
    }

    /**
     * <p>检索指定编目中可用的表列的描述。</p>
     * <p>只返回与目录、模式、表和列名条件匹配的列描述。</p>
     * <p>它们按TABLE_CAT、table_schema、TABLE_NAME和ORDINAL_POSITION排序。</p>
     *
     * @param catalog           目录名称;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schemaPattern     模式名称模式;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param tableNamePattern  一个表名模式;必须匹配存储在数据库中的表名
     * @param columnNamePattern 列名模式;必须匹配存储在数据库中的列名
     * @return 每行是一个列描述
     */
    public List<TableColumnInfo> getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) {
        String logFormat = "获取表字段信息失败。[catalog = {}, schemaPattern = {}, tableNamePattern = {}, columnNamePattern = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", catalog, schemaPattern, tableNamePattern);
            columnInfoMap = HylfFunUtil.mapAddValue(columnInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern),
                        logFormat, catalog, schemaPattern, tableNamePattern, columnNamePattern);
                List<TableColumnInfo> columnInfoList = HylfDataUtil.autoSetValue(resultSet, TableColumnInfo.class);
                for (TableColumnInfo tableColumnInfo : columnInfoList) {
                    String tableCat = tableColumnInfo.getTableCat();
                    String tableSchem = tableColumnInfo.getTableSchem();
                    String tableName = tableColumnInfo.getTableName();
                    String columnName = tableColumnInfo.getColumnName();
                    tableColumnInfo.setColumnPrivilegeInfoList(getColumnPrivileges(tableCat, tableSchem, tableName, columnName));
                }
                HylfFunUtil.autoClose(resultSet);
                return columnInfoList;
            });
            return columnInfoMap.get(key);
        });
    }

    /**
     * <p>检索指定编目中可用的表列的描述。</p>
     * <p>只返回与目录、模式、表和列名条件匹配的列描述。</p>
     * <p>它们按TABLE_CAT、table_schema、TABLE_NAME和ORDINAL_POSITION排序。</p>
     *
     * @param catalog          目录名称;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schemaPattern    模式名称模式;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param tableNamePattern 一个表名模式;必须匹配存储在数据库中的表名
     * @return 每行是一个列描述
     */
    public List<TableColumnInfo> getColumns(String catalog, String schemaPattern, String tableNamePattern) {
        return getColumns(catalog, schemaPattern, tableNamePattern, "%");
    }

    /**
     * <p>检索指定编目中可用的表列的描述。</p>
     * <p>只返回与目录、模式、表和列名条件匹配的列描述。</p>
     * <p>它们按TABLE_CAT、table_schema、TABLE_NAME和ORDINAL_POSITION排序。</p>
     *
     * @param tableNamePattern  一个表名模式;必须匹配存储在数据库中的表名
     * @param columnNamePattern 列名模式;必须匹配存储在数据库中的列名
     * @return 每行是一个列描述
     */
    public List<TableColumnInfo> getColumns(String tableNamePattern, String columnNamePattern) {
        return getColumns(getCatalog(), getSchema(), tableNamePattern, columnNamePattern);
    }

    /**
     * <p>检索指定编目中可用的表列的描述。</p>
     * <p>只返回与目录、模式、表和列名条件匹配的列描述。</p>
     * <p>它们按TABLE_CAT、table_schema、TABLE_NAME和ORDINAL_POSITION排序。</p>
     *
     * @param tableNamePattern 一个表名模式;必须匹配存储在数据库中的表名
     * @return 每行是一个列描述
     */
    public List<TableColumnInfo> getColumns(String tableNamePattern) {
        return getColumns(getCatalog(), getSchema(), tableNamePattern, "%");
    }

    /**
     * <p>检索表列的访问权限描述。</p>
     * <p>只返回与列名条件匹配的特权。它们按COLUMN_NAME和PRIVILEGE排序。</p>
     *
     * @param catalog           目录名称;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schema            模式名;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param table             表名;必须匹配存储在数据库中的表名
     * @param columnNamePattern 列名模式;必须匹配存储在数据库中的列名
     * @return 每一行都是一列权限描述
     */
    public List<PrivilegeInfo> getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) {
        String logFormat = "获取表字段访问权限描述失败。[catalog = {}, schema = {}, table = {}, columnNamePattern = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", catalog, schema, table, columnNamePattern);
            columnPrivilegeInfoMap = HylfFunUtil.mapAddValue(columnPrivilegeInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getColumnPrivileges(catalog, schema, table, columnNamePattern)
                        , logFormat, catalog, schema, table, columnNamePattern);
                List<PrivilegeInfo> columnPrivilegeInfoList = HylfDataUtil.autoSetValue(resultSet, PrivilegeInfo.class);
                HylfFunUtil.autoClose(resultSet);
                return columnPrivilegeInfoList;
            });
            return columnPrivilegeInfoMap.get(key);
        });
    }

    /**
     * <p>检索表列的访问权限描述。</p>
     * <p>只返回与列名条件匹配的特权。它们按COLUMN_NAME和PRIVILEGE排序。</p>
     *
     * @param table             表名;必须匹配存储在数据库中的表名
     * @param columnNamePattern 列名模式;必须匹配存储在数据库中的列名
     * @return 每一行都是一列权限描述
     */
    public List<PrivilegeInfo> getColumnPrivileges(String table, String columnNamePattern) {
        return getColumnPrivileges(getCatalog(), getSchema(), table, columnNamePattern);
    }

    /**
     * <p>检索表列的访问权限描述。</p>
     * <p>只返回与列名条件匹配的特权。它们按COLUMN_NAME和PRIVILEGE排序。</p>
     *
     * @param table 表名;必须匹配存储在数据库中的表名
     * @return 每一行都是一列权限描述
     */
    public List<PrivilegeInfo> getColumnPrivileges(String table) {
        return getColumnPrivileges(getCatalog(), getSchema(), table, "%");
    }
}
